package org.acm.seguin.refactor.type;
import java.util.Enumeration;
import java.util.Vector;
import org.acm.seguin.refactor.EliminatePackageImportVisitor;
import org.acm.seguin.refactor.Refactoring;
import org.acm.seguin.refactor.RefactoringException;
import org.acm.seguin.summary.FileSummary;
import org.acm.seguin.summary.PackageSummary;
import org.acm.seguin.summary.Summary;
import org.acm.seguin.summary.TypeSummary;
import org.acm.seguin.summary.query.GetTypeSummary;
/**
* Adds a class that is either a parent or a child of an existing class.
*
*@author Chris Seguin
*/
public abstract class AddClassRefactoring extends Refactoring {
private Vector summaryList;
private String className = null;
/**
* Constructor for the AddClassRefactoring object
*/
public AddClassRefactoring() {
summaryList = new Vector();
}
/**
* Sets the name of the new class
*
*@param value the name of the new class
*/
protected void setNewClassName(String value) {
className = value;
}
/**
* Gets the name of the new class
*
*@return the name
*/
protected String getNewClassName() {
return className;
}
/**
* Adds a target class - either the parent or the child, depending on what
* we are adding
*
*@param summary the summary to be extended
*/
protected void addTargetClass(TypeSummary summary) {
if (summary != null) {
summaryList.addElement(summary);
}
}
/**
* Describes the preconditions that must be true for this refactoring to be
* applied
*
*@exception RefactoringException thrown if one or more of the
* preconditions is not satisfied. The text of the exception provides a
* hint of what went wrong.
*/
protected void preconditions() throws RefactoringException {
if (summaryList.size() == 0) {
throw new RefactoringException("Unable to find type to extend");
}
if (className == null) {
throw new RefactoringException("New class name is not specified");
}
// Get the package
PackageSummary summary = getPackageSummary((Summary) summaryList.elementAt(0));
TypeSummary abstractParent = GetTypeSummary.query(summary, className);
if (abstractParent != null) {
throw new RefactoringException("Type with that name already exists");
}
TypeSummary anySummary =
(TypeSummary) summaryList.elementAt(0);
TypeSummary originalParent =
GetTypeSummary.query(anySummary.getParentClass());
Enumeration enum_ = summaryList.elements();
while (enum_.hasMoreElements()) {
TypeSummary typeSummary = (TypeSummary) enum_.nextElement();
if (typeSummary.isInterface()) {
throw new RefactoringException("This refactoring only works for classes, not interfaces");
}
FileSummary fileSummary = (FileSummary) typeSummary.getParent();
TypeSummary referenced = GetTypeSummary.query(fileSummary, className);
if (referenced != null) {
throw new RefactoringException("New class already exists relative to " + typeSummary.getName());
}
if (!isSameParent(originalParent,
GetTypeSummary.query(typeSummary.getParentClass()))) {
throw new RefactoringException("Existing types don't share the same original parent");
}
}
}
/**
* Performs the transform on the rest of the classes
*/
protected void transform() {
Enumeration enum_ = summaryList.elements();
while (enum_.hasMoreElements()) {
TypeSummary typeSummary = (TypeSummary) enum_.nextElement();
transformOriginal(typeSummary);
}
createClass((TypeSummary) summaryList.elementAt(0), className);
EliminatePackageImportVisitor epiv = new EliminatePackageImportVisitor(getComplexTransform());
epiv.setPackageSummary(getPackageSummary((Summary) summaryList.elementAt(0)));
epiv.visit(null);
}
/**
* Creates a class
*
*@param existingType the existing type
*@param className the name of the new class
*/
protected abstract void createClass(TypeSummary existingType, String className);
/**
* Transforms the original AST
*
*@param typeSummary the particular type that is being changed
*/
protected abstract void transformOriginal(TypeSummary typeSummary);
/**
* Gets the package summary
*
*@param base Description of Parameter
*@return the package summary
*/
private PackageSummary getPackageSummary(Summary base) {
Summary current = base;
while (!(current instanceof PackageSummary)) {
current = current.getParent();
}
return (PackageSummary) current;
}
/**
* Gets the SameParent attribute of the AddClassRefactoring object
*
*@param one Description of Parameter
*@param two Description of Parameter
*@return The SameParent value
*/
private boolean isSameParent(TypeSummary one, TypeSummary two) {
if (isObject(one)) {
return isObject(two);
}
if (isObject(two)) {
return false;
}
return one.equals(two);
}
/**
* Gets the Object attribute of the AddClassRefactoring object
*
*@param item Description of Parameter
*@return The Object value
*/
private boolean isObject(TypeSummary item) {
if (item == null) {
return true;
}
if (item.getName().equals("Object")) {
return true;
}
return false;
}
}